/**************************************************************************//**
 * @file     drv_lcd.c
 * @version  V1.00
 * $Revision: 1 $
 * $Date: 16/02/17 2:44p $
 * @brief    NUC970 LCM driver source file
 *
 * @note
 * SPDX-License-Identifier: Apache-2.0
 * @Copyright (C) 2021 THEWON. All rights reserved.
*****************************************************************************/
#include <board.h>

#ifdef BSP_USING_LCD
#include "nuc970.h"
#include "nuc970_lcd.h"
#include "nuc970_2d.h"
#include "nuc970_mfp.h"
#include "lcd_port.h"
#include "drv_sys.h"
#include "drv_gpio.h"

//#define DRV_DEBUG
#define LOG_TAG              "drv.lcd"
#include <drv_log.h>

#ifdef BSP_USING_GE2D
static unsigned long make_color(int color)
{
    UINT32 r, g, b;

#if LCD_BITS_PER_PIXEL == 8
        r = (color & 0x00E00000) >> 16; // 3 bits
        g = (color & 0x0000E000) >> 11; // 3 bits
        b = (color & 0x000000C0) >> 6;  // 2 bits
        return (r | g | b);
#elif LCD_BITS_PER_PIXEL == 16
        r = (color & 0x00F80000) >> 8; // 5 bits
        g = (color & 0x0000FC00) >> 5; // 6 bits
        b = (color & 0x000000F8) >> 3; // 5 bits
        return (r | g | b);
#else 
    return (UINT32)color;
#endif
}
#endif

struct drv_lcd_device
{
    struct rt_device parent;

    struct rt_device_graphic_info lcd_info;

    struct rt_semaphore lcd_lock;

    /* 0:front_buf is being used 1: back_buf is being used*/
    rt_uint8_t cur_buf;
    rt_uint8_t *front_buf;
    rt_uint8_t *back_buf;
};

UINT8 *LCDFrameBufPtr, *u8OSDFrameBufPtr;

static void lcmIntHandler(int vector, void *param)
{
    /* clear VPOST interrupt state */
    UINT32 uintstatus;

    uintstatus = inpw(REG_LCM_INT_CS);
    if (uintstatus & VPOSTB_UNDERRUN_INT)
    {
        outpw(REG_LCM_INT_CS, inpw(REG_LCM_INT_CS) | VPOSTB_UNDERRUN_INT);
        LOG_E("LCD under run error!!");
    }
    else if (uintstatus & VPOSTB_BUS_ERROR_INT)
    {
        outpw(REG_LCM_INT_CS, inpw(REG_LCM_INT_CS) | VPOSTB_BUS_ERROR_INT);
        LOG_E("LCD bus error!!");
    }
}

void turn_off_lcd_backlight(void)
{
#if defined (USE_E50A2V1)
#elif defined (USE_TM043) | defined (USE_AT043)
    rt_pin_mode(LCD_DISP_GPIO_NUM, PIN_MODE_OUTPUT_PULLUP);

    rt_pin_write(LCD_DISP_GPIO_NUM, PIN_LOW);
#endif
}

void turn_on_lcd_backlight(void)
{
#if defined (USE_E50A2V1)
#elif defined (USE_TM043) | defined (USE_AT043)
    rt_pin_mode(LCD_DISP_GPIO_NUM, PIN_MODE_OUTPUT_PULLUP);

    rt_pin_write(LCD_DISP_GPIO_NUM, PIN_HIGH);
#endif
}

rt_err_t nuc97x_lcd_init(struct drv_lcd_device *lcd)
{
    return RT_EOK;
}

int drv_lcd_hw_init(void)
{
    rt_err_t result = RT_EOK;

    MFP_LCMInit();

#if defined (USE_E50A2V1)
#elif defined (USE_TM043)
    rt_pin_mode(LCD_DE_GPIO_NUM, PIN_MODE_OUTPUT_PULLDOWN);
    rt_pin_write(LCD_DE_GPIO_NUM, PIN_LOW);
#endif

    // outpw(REG_LCM_DCCS, inpw(REG_LCM_DCCS) & ~(VPOSTB_DISP_OUT_EN | VPOSTB_VA_EN));
    // outpw(REG_LCM_OSD_WINS, 0);
    // outpw(REG_LCM_OSD_WINE, 0);
    // outpw(REG_LCM_OSD_OVERLAY, inpw(REG_LCM_OSD_OVERLAY) & 0xFFFFFFF0);
    // outpw(REG_LCM_DCCS, inpw(REG_LCM_DCCS) & (~VPOSTB_OSD_EN));

    // Init LCD interface for E50A2V1 LCD module
    vpostLCMInit(&VPOST_TYPE);

    // Set scale to 1:1
    vpostVAScalingCtrl(1, 0, 1, 0, VA_SCALE_INTERPOLATION);

    // Set display color depth
    vpostSetVASrc(VASRC_TYPE);

    // Get pointer of video frame buffer
    // Note: before get pointer of frame buffer, must set display color depth first
    LCDFrameBufPtr = vpostGetFrameBuffer();
    if(LCDFrameBufPtr == NULL)
    {
        MFP_LCMDeInit();
        result = -RT_ERROR;
    }

    rt_hw_interrupt_install(LCD_IRQn, lcmIntHandler, RT_NULL, "lcm");
    rt_hw_interrupt_set_priority(LCD_IRQn, IRQ_LEVEL_6);
    rt_hw_interrupt_umask(LCD_IRQn);

    outpw(REG_LCM_DCCS, inpw(REG_LCM_DCCS) | VPOSTB_DISP_INT_EN);
    outpw(REG_LCM_INT_CS, inpw(REG_LCM_INT_CS) | VPOSTB_UNDERRUN_EN);
#ifdef BSP_USING_GE2D
// init ge2d
    MFP_GE2DInit();
    outpw(REG_GE2D_XYSORG, (unsigned int)LCDFrameBufPtr);
    outpw(REG_GE2D_XYDORG, (unsigned int)LCDFrameBufPtr);
    outpw(REG_GE2D_WRPLNMSK, 0x00FFFFFF); // write plane mask
    outpw(REG_GE2D_MISCTL, 0x10);
// clear screen
    outpw(REG_GE2D_CTL, 0xCC410040);
    outpw(REG_GE2D_BGCOLR, make_color(0)); // fill with background color

    outpw(REG_GE2D_SDPITCH, VPOST_TYPE.u32DevWidth << 16);

    outpw(REG_GE2D_DSTSPA, 0); // starts from (0,0)

    outpw(REG_GE2D_RTGLSZ, VPOST_TYPE.u32DevHeight << 16 | VPOST_TYPE.u32DevWidth);

    outpw(REG_GE2D_TRG, 1);
    while((inpw(REG_GE2D_INTSTS) & 0x01) == 0); // wait for command complete
    outpw(REG_GE2D_INTSTS, 1); // clear interrupt status
// clear screen end
#endif
    // Start to display
    if((inpw(REG_LCM_DCCS) & VPOSTB_SINGLE) == VPOSTB_SINGLE)
        while((inpw(REG_LCM_DCCS) & VPOSTB_VA_EN) == VPOSTB_VA_EN);//wait VA_EN low
    outpw(REG_LCM_DCCS, inpw(REG_LCM_DCCS) | VPOSTB_DISP_OUT_EN); //display_out-enable
    outpw(REG_LCM_DCCS, inpw(REG_LCM_DCCS) | VPOSTB_VA_EN); //va-enable

    while((inpw(REG_LCM_DCCS) & VPOSTB_DISP_ON) != VPOSTB_DISP_ON){;}//wait VA_EN low

    return result;
}
INIT_DEVICE_EXPORT(drv_lcd_hw_init);

#endif /* BSP_USING_LCD */
